//
//  SerialFaker.m
//  SerialFaker
//
//  Created by crash-x on 31.10.08.
//


#import <mach-o/nlist.h>
#import <CoreFoundation/CoreFoundation.h>
#import <CoreFoundation/CFString.h>
#import <IOKit/IOKitLib.h>
#import <Foundation/NSDictionary.h>

#define APP_NAME "SerialFaker"
#define LOG_ERROR 1
#define LOG_WARNING 2
#define LOG_INFO 3
#define LOG_DEBUG 4
#define IOKit "/System/Library/Frameworks/IOKit.framework/IOKit"
#define CONFIG_PATH "/Library/MobileSubstrate/DynamicLibraries/SerialFaker.plist"

extern void MSHookFunction(void *symbol, void *replace, void **result);



CFTypeRef (*__orig__IORegistryEntryCreateCFProperty)(io_registry_entry_t entry,
												   CFStringRef key,
												   CFAllocatorRef allocator,
												   IOOptionBits options) = 0x0;
CFStringRef theFakeSerial;

NSInteger myLogLevel = 0;


void myLog(int logLevel, NSString * formatString, ...) {
	NSString * logString;
	va_list argumentList;
	char *logLevelStrings[] = {"None", "Error", "Warning", "Info", "Debug"};

	if(myLogLevel != 0 && logLevel > myLogLevel)
		return;
	
	if(logLevel > 4 || logLevel == 0)
		return;
	
	
	va_start (argumentList, formatString);
	logString = [[NSString alloc] initWithFormat:formatString arguments:argumentList];
	va_end (argumentList);

	NSLog(@"%s:%s: %@", APP_NAME, logLevelStrings[logLevel], logString);
	
	[logString release];
}




CFTypeRef __IORegistryEntryCreateCFProperty(io_registry_entry_t entry,
										  CFStringRef key,
										  CFAllocatorRef allocator,
										  IOOptionBits options ) {
	
	if(CFStringCompare(key, (CFStringRef)@"IOPlatformSerialNumber", 0) == kCFCompareEqualTo) {
		myLog(LOG_DEBUG, @"__IORegistryEntryCreateCFProperty asking for Serial was called!");
		return theFakeSerial;
	} else {
		return __orig__IORegistryEntryCreateCFProperty(entry, key, allocator, options);
	}
}


__attribute__((constructor))
static void SerialFakerInitializer() {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	
	NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
	NSDictionary *config = [[NSDictionary alloc] initWithContentsOfFile:@CONFIG_PATH];
	
	if (config == nil) {
		myLog(LOG_ERROR, @"Wasnt able to open config file!");
		return;
	}
	
	NSString *tmp = [config objectForKey:@"logLevel"];
	if(tmp != nil) {
		myLogLevel = [tmp intValue];
		if(myLogLevel < 0 || myLogLevel > 4)
			myLogLevel = 0;
	} else {
		myLogLevel = 0;
	}
	
	myLog(LOG_DEBUG, @"Loading...");
	
	NSDictionary *serials = [config objectForKey:@"Serials"];
	
	if (serials == nil) {
		myLog(LOG_ERROR, @"There is no section with Serials in SerialFaker's config");
		return;
	}
	
	for(NSString *appKey in [serials allKeys]) {
		if([appKey compare: bundleIdentifier] == kCFCompareEqualTo) {
			theFakeSerial = CFStringCreateCopy(NULL, (CFStringRef)[serials objectForKey: appKey]);
			if (theFakeSerial == NULL) {
				myLog(LOG_ERROR, @"Wasn't able to create Serial String... failed loading lib in %@", bundleIdentifier);
				return;
			} else {
				break;
			}
		}
	}
									   
	
	struct nlist nl[3];
    memset(nl, 0x0, sizeof(nl));
    nl[0].n_un.n_name = (char *) "_IORegistryEntryCreateCFProperty";
	
    if(nlist(IOKit, nl) == 0) {
		MSHookFunction((void *)nl[0].n_value, (void *)&__IORegistryEntryCreateCFProperty, (void **)&__orig__IORegistryEntryCreateCFProperty);
		myLog(LOG_INFO, @"Hooked IORegistryEntryCreateCFProperty in %@", bundleIdentifier);
	} else {
		myLog(LOG_ERROR, @"Was not able to get the address of IORegistryEntryCreateCFProperty");
		myLog(LOG_ERROR, @"%@ was not hooked!", [[NSBundle mainBundle] bundleIdentifier]);
	}
	
	[pool release]; 
}
